package com.boot.security.service.cluster;

import com.boot.security.model.po.Resource;
import com.boot.security.model.vo.SourceKey;
import com.boot.security.service.authorization.AccessSourceClusterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 通过配置中心获取所有实例，HTTP通知更新 {@link DefaultAccessSourceClusterHandlerImpl }
 * @date 2021/8/1 22:30
 **/

@Slf4j
//@Service
public class DefaultAccessSourceClusterServiceImpl implements AccessSourceClusterService {

    private final DiscoveryClient client;

    private final ExecutorService executorService;

    private final RestTemplate restTemplate;

    @Value("${spring.application.name}")
    private String applicationName;

    public DefaultAccessSourceClusterServiceImpl(DiscoveryClient client, ExecutorService executorService, ClientHttpRequestFactory httpRequestFactory) {
        this.client = client;
        this.executorService = executorService;
        this.restTemplate = new RestTemplate(httpRequestFactory);
    }

    @Override
    public void saveOne(SourceKey sourceKey, Long resourceId) {
        List<ServiceInstance> serviceInstances = client.getInstances(applicationName);
        Queue<Future<Boolean>> futures = new LinkedList<>();
        serviceInstances.forEach(serviceInstance -> {
            System.out.println(serviceInstance.getUri());
            Future<Boolean> future = executorService.submit(new Callable<Boolean>() {

                HttpStatus execute() {
                    try {
                        return restTemplate.exchange(new URI(serviceInstance.getUri() + "/cluster/save"),
                                HttpMethod.POST, new HttpEntity<>(AccessSourceClusterChange.builder()
                                        .sourceKey(sourceKey)
                                        .resourceId(resourceId)
                                        .build()), Void.class).getStatusCode();
                    } catch (URISyntaxException e) {
                        log.warn(e.getMessage());
                        return HttpStatus.BAD_REQUEST;
                    }
                }

                @Override
                public Boolean call() {
                    if (HttpStatus.OK != execute()) {
                        if (HttpStatus.OK != execute()) {
                            return HttpStatus.OK == execute();
                        }
                    }
                    return true;
                }
            });
            futures.add(future);
        });
        while (!futures.isEmpty()) {
            Future<Boolean> future = futures.poll();
            try {
                Boolean result = future.get();
                if (!result) {
                    futures.offer(future);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.warn(e.getMessage());
            }
        }
    }

    @Override
    public void saveMulti(SourceKey sourceKey, List<Long> resourceIds) {
        List<ServiceInstance> serviceInstances = client.getInstances(applicationName);
        Queue<Future<Boolean>> futures = new LinkedList<>();
        serviceInstances.forEach(serviceInstance -> {
            System.out.println(serviceInstance.getUri());
            Future<Boolean> future = executorService.submit(new Callable<Boolean>() {

                HttpStatus execute() {
                    try {
                        return restTemplate.exchange(new URI(serviceInstance.getUri() + "/cluster/save-list"),
                                HttpMethod.POST, new HttpEntity<>(AccessSourceClusterChange.builder()
                                        .sourceKey(sourceKey)
                                        .resourceIds(resourceIds)
                                        .build()), Void.class).getStatusCode();
                    } catch (URISyntaxException e) {
                        log.warn(e.getMessage());
                        return HttpStatus.BAD_REQUEST;
                    }
                }

                @Override
                public Boolean call() {
                    if (HttpStatus.OK != execute()) {
                        if (HttpStatus.OK != execute()) {
                            return HttpStatus.OK == execute();
                        }
                    }
                    return true;
                }
            });
            futures.add(future);
        });
        while (!futures.isEmpty()) {
            Future<Boolean> future = futures.poll();
            try {
                Boolean result = future.get();
                if (!result) {
                    futures.offer(future);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.warn(e.getMessage());
            }
        }
    }

    @Override
    public void updateOne(SourceKey sourceKey, Resource oldResource) {
        List<ServiceInstance> serviceInstances = client.getInstances(applicationName);
        Queue<Future<Boolean>> futures = new LinkedList<>();
        serviceInstances.forEach(serviceInstance -> {
            System.out.println(serviceInstance.getUri());
            Future<Boolean> future = executorService.submit(new Callable<Boolean>() {

                HttpStatus execute() {
                    try {
                        return restTemplate.exchange(new URI(serviceInstance.getUri() + "/cluster/update"),
                                HttpMethod.POST, new HttpEntity<>(AccessSourceClusterChange.builder()
                                        .sourceKey(sourceKey)
                                        .resource(oldResource)
                                        .build()), Void.class).getStatusCode();
                    } catch (URISyntaxException e) {
                        log.warn(e.getMessage());
                        return HttpStatus.BAD_REQUEST;
                    }
                }

                @Override
                public Boolean call() {
                    if (HttpStatus.OK != execute()) {
                        if (HttpStatus.OK != execute()) {
                            return HttpStatus.OK == execute();
                        }
                    }
                    return true;
                }
            });
            futures.add(future);
        });
        while (!futures.isEmpty()) {
            Future<Boolean> future = futures.poll();
            try {
                Boolean result = future.get();
                if (!result) {
                    futures.offer(future);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.warn(e.getMessage());
            }
        }
    }

    @Override
    public void removeOne(SourceKey sourceKey, Resource oldResource) {
        List<ServiceInstance> serviceInstances = client.getInstances(applicationName);
        Queue<Future<Boolean>> futures = new LinkedList<>();
        serviceInstances.forEach(serviceInstance -> {
            Future<Boolean> future = executorService.submit(new Callable<Boolean>() {

                HttpStatus execute() {
                    try {
                        return restTemplate.exchange(new URI(serviceInstance.getUri() + "/cluster/remove"),
                                HttpMethod.POST, new HttpEntity<>(AccessSourceClusterChange.builder()
                                        .sourceKey(sourceKey)
                                        .resource(oldResource)
                                        .build()), Void.class).getStatusCode();
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                        return HttpStatus.BAD_REQUEST;
                    }
                }

                @Override
                public Boolean call() {
                    if (HttpStatus.OK != execute()) {
                        if (HttpStatus.OK != execute()) {
                            return HttpStatus.OK == execute();
                        }
                    }
                    return true;
                }
            });
            futures.add(future);
        });
        while (!futures.isEmpty()) {
            Future<Boolean> future = futures.poll();
            try {
                Boolean result = future.get();
                if (!result) {
                    futures.offer(future);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.warn(e.getMessage());
            }
        }
    }

    @Override
    public void removeMulti(SourceKey sourceKey, List<Resource> resources) {
        List<ServiceInstance> serviceInstances = client.getInstances(applicationName);
        Queue<Future<Boolean>> futures = new LinkedList<>();
        serviceInstances.forEach(serviceInstance -> {
            System.out.println(serviceInstance.getUri());
            Future<Boolean> future = executorService.submit(new Callable<Boolean>() {

                HttpStatus execute() {
                    try {
                        return restTemplate.exchange(
                                new URI(serviceInstance.getUri() + "/cluster/remove-list"),
                                HttpMethod.POST, new HttpEntity<>(AccessSourceClusterChange.builder()
                                        .sourceKey(sourceKey)
                                        .resources(resources)
                                        .build()), Void.class).getStatusCode();
                    } catch (URISyntaxException e) {
                        log.warn(e.getMessage());
                        return HttpStatus.BAD_REQUEST;
                    }
                }

                @Override
                public Boolean call() {
                    if (HttpStatus.OK != execute()) {
                        if (HttpStatus.OK != execute()) {
                            return HttpStatus.OK == execute();
                        }
                    }
                    return true;
                }
            });
            futures.add(future);
        });
        while (!futures.isEmpty()) {
            Future<Boolean> future = futures.poll();
            try {
                Boolean result = future.get();
                if (!result) {
                    futures.offer(future);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.warn(e.getMessage());
            }
        }
    }
}
